 /*
基于/libs/request 的二次封装，简化配置，拓展功能
用法：
const ajax = new Ajax({
  name: 'doorway', 
  baseURL: https://bestWehoetl.com,
  successCodes: ['2xx'], 
  reponseMsgKey: 'msg',
  reponseCodeKey: 'code',
  reponseSuccessCodes: [100]
})

ajax.setConfig({ header: { Authorization: 'JDkdd\434jfk4584593mk87843n' } }) // 设置全局config, 如header等信息

ajax.get('/api/getList', { id: 1 })

 */

import Request from 'luch-request'
import sdk from 'sdk'
import store from '@/store'
// import obj2query from './obj2query'

const LOADING_MIN_DURATION = 500
const REQUIRED_KEYS = ['baseURL', 'name']
const METHOD_KEYS = ['get', 'post', 'upload', 'delete', 'connect', 'delete', 'head', 'options', 'trace']

function checkConfig(config) {
  if (!(config instanceof Object)) {
  throw new Error('必须传入配置')
  } else {
  REQUIRED_KEYS.forEach(key => {
    if (!config[key]) {
    throw new Error(`必须配置${key}`)
    }
  })
  }
}

const checkCodes = (codes, statusCode) => {
  return codes.some((code) =>{
    const codeStr = code + ''
    const regSrt = `^${codeStr.replace(/x/g, '\\d')}$`
    const reg = new RegExp(regSrt)
    return reg.test(statusCode)
  }) 
}

class AJAX {
  /**
   * 构建ajax实例
   * @param {object} config 全局配置对象
   * @param {string} config.name *必配，实例名称
   * @param {string} config.baseURL *必配，基础域名
   * @param {Number[]} [config.successCodes] 选配，成功状态码，若配置validateStatus则使用validateStatus，不配则表示都成功
   * @param {string} [config.reponseMsgKey='msg'] 选配，默认'msg'，response中message的key
   * @param {string} [config.reponseCodeKey='code'] 选配，默认'code'，response中code的key
   * @param {Number[]} [config.reponseSuccessCodes] 选配，reponse返回的成功代码，不配则表示都成功
   * ...其他luch-request config
   * 支持luch-request配置，详见 https://www.quanzhan.co/luch-request/guide/3.x/#%E5%8F%AF%E9%85%8D%E7%BD%AE%E9%A1%B9
   */
  constructor(config) {
    checkConfig(config)

    const { 
      name, 
      successCodes, 
      reponseMsgKey, 
      reponseCodeKey, 
      reponseSuccessCodes,
      ...requestConfig
    } = config
    
    this._config = config

    this._setValidateStatusConfig(requestConfig, successCodes)
    
    this.requestInstance = new Request(requestConfig)

    this._initRequestMethods()
  }

  setConfig(options = {}) {
    this.requestInstance.setConfig((config) => {
      options.header = { ...config.header, ...options.header } 
      return {
        ...config,
        ...options
      }
    })
  }

  /**
   * 初始化请求方法
   * 为实例注入get, post, upload, delete, connect, delete, head, options, trace方法
   */
  _initRequestMethods() {
    METHOD_KEYS.forEach((method) => {
      this[method] = async (...params) => {
        return this._fetch(method, ...params)
      }
    })
  }

  _setValidateStatusConfig(requestConfig, codes) {
    if (codes && codes.length && !requestConfig.validateStatus) {
      requestConfig.validateStatus = (statusCode) => {
        return checkCodes(codes, statusCode)
      }
    }
  }

  _handleResponseCodes(response, path) {
    const { reponseSuccessCodes, reponseCodeKey, reponseMsgKey, name } = this._config
    const code = response[reponseCodeKey]
    const msg = response[reponseMsgKey] || ''
    if (!reponseSuccessCodes || !reponseSuccessCodes.length) return
    const isSuccess = checkCodes(reponseSuccessCodes, code)

    // 后端code返回：
    // 0-请求成功
    // 400-参数错误、底层服务调用错误
    // 405-用户token异常
    // 500-服务器异常
    // 大于等于6位数的code都是业务code，如110001
    if (!isSuccess) {
      // 1. 封装请求错误类，先简单处理
      // 2. 弹出错误
      // 3. 请求超时处理
      
      // token异常，拦截登录
      if (code === 405) {
        store.commit('REMOVE_TOKEN')

        // 调起登录界面
        store.dispatch('LOGIN', {
          forceLogin: true
        })
      // #ifdef MP
      } else if(code === 1100429) { // 小程序，用户未注册
        store.commit('REMOVE_TOKEN')

        sdk.navigateTo({
          url: '/pages/register/index'
        })
      // #endif
     
      } else if (code === 400092) { // 库存不足
        sdk.showToast({
          icon: 'none',
          title: '当前库存不足，请更改其他规格~',
          duration: 2000
        })
      } else if (code === 400093) { // 下单过程中商品售罄
        sdk.showToast({
          icon: 'none',
          title: '当前商品已下架，请重新选择~',
          duration: 2000
        })
      }else {
        sdk.showToast({
            icon: 'none',
            title: '当前访问人数过多，请稍后再试~',
            duration: 2000
        })
      }
      
      const errMsg = `[${name}-请求失败]: code: ${code}, msg: ${msg}`
      console.error(errMsg)
      throw new Error(errMsg)
    }
  }

  /**
   * 请求方法封装，加入loading处理
   * @param {string} method 请求方法
   * @param {string} path 请求路径
   * @param {object} params 请求入参
   * @returns response
   * @memberof AJAX
   */
  async _fetch(method, path, ...rest) {
    const config = rest[rest.length - 1] || {}
    // params = params || {}
    const custom = config.custom || {}
    const { loadingType, loadingDuration } = custom
    
    // 开始loading
    this._loading(loadingType)


    const response = await this.requestInstance[method](path, ...rest)
    .catch((err) => {
      if (err.errMsg) {
        // TODO 错误处理
        // modal.toast(errorMessage)
      }
      // 500 报错
      sdk.showToast({
          icon: 'none',
          title: '当前访问人数过多，请稍后再试~',
          duration: 2000
      })
      throw err
    })

    // let response
    // if (method === 'post') {
    //   // 执行请求
    //   response = await this.requestInstance[method](path, params, config)
    //   .catch((err) => {
    //     if (err.errMsg) {
    //       // TODO 错误处理
    //       // modal.toast(errorMessage)
    //     }
    //     throw err
    //   })
    // } else {
    //   // 执行请求
    //   response = await this.requestInstance[method](path, {
    //     ...config,
    //     params
    //   }).catch((err) => {
    //     if (err.errMsg) {
    //       // TODO 错误处理
    //       // modal.toast(errorMessage)
    //     }
    //     throw err
    //   })
    // }

    // 关闭loading
    this._hideLoading(loadingType, loadingDuration)

    // reponseSuccessCodes处理
    this._handleResponseCodes(response, path)
    
    return response 
  }

  _loading(type) {
    // [TODO] 优化loading配置
    if (type === 'none' || !type) return
    if (type === 'navigate') {
      sdk.showNavigationBarLoading()
    } else {
      this._startLoadingTime = Date.now()
      // [TODO]
      // modal.loading()
    }
  }

  async _hideLoading(type, loadingDuration = LOADING_MIN_DURATION) {
    if (type === 'none' || !type) return
    if (type === 'navigate') {
      sdk.hideNavigationBarLoading()
    } else {
      const duration = Date.now() - this._startLoadingTime
      if (duration < loadingDuration) {
        await sleep(loadingDuration - duration)
      }
      // [TODO]
      // modal.hideLoading()
    }
  } 
}

export default AJAX
